package com.ynet.middleground.approve.model;

import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.zip.ZipInputStream;

import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.lang3.StringUtils;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ynet.core.common.PagerInfo;
import com.ynet.core.exception.BusinessException;
import com.ynet.middleground.approve.bean.ActivateProcessReq;
import com.ynet.middleground.approve.bean.DeleteProcessReq;
import com.ynet.middleground.approve.bean.DeployProcessReq;
import com.ynet.middleground.approve.bean.QueryProcessReqPager;
import com.ynet.middleground.approve.constant.ProcessStatusEnum;
import com.ynet.middleground.approve.dto.ProcessDefinitionDTO;
import com.ynet.middleground.approve.utils.BeanUtil;
import com.ynet.middleground.approve.utils.ErrorCodeUtil;

/**
 * @author liulx
 * @description 流程定义相关业务处理
 * @date 2019-10-20 19:39
 */
@Component
public class ProcessModel {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private Mapper dozerBeanMapper;

    @Autowired
    private ErrorCodeUtil errorCodeUtil;

    @Autowired
    private HistoryService historyService;

    /**
     * 部署流程 bpmn和png 压缩后的zip文件
     *
     *
     * @param deployProcessReq
     *            部署工作流规则文件对象: {文件流字节}
     * @return 部署ID
     */
    public String saveProcess(DeployProcessReq deployProcessReq) {
        ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(deployProcessReq.getBytes()));
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment().addZipInputStream(zipInputStream);
        Deployment deployment = deploymentBuilder.deploy();
        return deployment.getId();
    }

    /**
     * 根据流程部署ID删除流程
     *
     * @param deleteProcessReq
     *            删除流程定义请求对象: {流程部署ID, 级联删除标志}
     */
    public void removeProcessByDeploymentId(DeleteProcessReq deleteProcessReq) {
        // 删除前，先校验流程是否存在
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
            .deploymentId(deleteProcessReq.getDeploymentId()).singleResult();
        if (processDefinition == null) {
            throw new BusinessException(errorCodeUtil.getValue("ECAP0100"), "ECAP0100");
        }
        if (!deleteProcessReq.isCascade()) {
            long count = historyService.createHistoricProcessInstanceQuery()
                .deploymentId(deleteProcessReq.getDeploymentId()).count();
            if (count > 0) {
                throw new BusinessException(errorCodeUtil.getValue("ECAP0103"), "ECAP0103");
            }
        }
        repositoryService.deleteDeployment(deleteProcessReq.getDeploymentId(), deleteProcessReq.isCascade());
    }

    /**
     * 分页查询流程定义列表
     *
     * @param reqPager
     *            查询流程对象：{分页对象,流程名称}
     * @return 流程定义列表
     */
    public List<ProcessDefinitionDTO> listProcessByPage(QueryProcessReqPager reqPager) {
        PagerInfo pagerInfo = reqPager.getPagerInfo();
        // 根据是否要按流程名称查询生成不同的查询对象
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        if (StringUtils.isNotBlank(reqPager.getProcessName())) {
            query = query.processDefinitionNameLike("%" + reqPager.getProcessName() + "%");
        }
        if (StringUtils.isNotBlank(reqPager.getCategory())) {
            query = query.processDefinitionCategoryLike("%" + reqPager.getCategory() + "%");
        }
        query = query.latestVersion().orderByProcessDefinitionVersion().desc();

        // 计算应该从第几条记录去取
        int start = (pagerInfo.getPageNum() - 1) * pagerInfo.getPageSize();

        // 流程定义列表
        List<ProcessDefinition> queryList = query.listPage(start, pagerInfo.getPageSize());
        pagerInfo.setTotal(query.count());
        return BeanUtil.mapList(dozerBeanMapper, queryList, ProcessDefinitionDTO.class);
    }

    /**
     * 挂起或激活指定流程定义
     *
     * @param processReq
     *            激活流程对象: {流程定义id, 激活挂起标志}
     */
    public void suspendOrActivateProcessDefinition(ActivateProcessReq processReq) {
        String processDefinitionId = processReq.getProcessDefinitionId();
        if (StringUtils.isBlank(processDefinitionId)) {
            throw new BusinessException(errorCodeUtil.getValue("ECAP0001", "流程定义ID"), "ECAP0001");
        }
        int type = processReq.getType();
        // 获得流程定义
        ProcessDefinition processDefinition =
            repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();

        if (processDefinition == null) {
            throw new BusinessException(errorCodeUtil.getValue("ECAP0100"), "ECAP0100");
        }
        // 是否挂起
        boolean suspend = processDefinition.isSuspended();
        if (suspend) {
            if (type == ProcessStatusEnum.ACTIVE.getCode()) {
                // 如果暂挂起则激活
                repositoryService.activateProcessDefinitionById(processDefinitionId, false, null);
            } else {
                throw new BusinessException(errorCodeUtil.getValue("ECAP0101"), "ECAP0101");
            }
        } else {
            if (type == ProcessStatusEnum.SUSPEND.getCode()) {
                // 如果激活则挂起
                repositoryService.suspendProcessDefinitionById(processDefinitionId, false, null);
            } else {
                throw new BusinessException(errorCodeUtil.getValue("ECAP0102"), "ECAP0102");
            }
        }
    }

    /**
     * 通过流程定义ID获取流程定义信息
     * 
     * @param processDefinitionId
     *            流程定义ID
     * @return 流程定义对象
     */
    public ProcessDefinition getProcessDefinitionInfo(String processDefinitionId) {
        return repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
    }

}
